package net.cyndeline.events.actions

import net.cyndeline.entitySystem.Event
import net.cyndeline.AttackType
import net.cyndeline.ElementType
import net.cyndeline.Physical

/**
 * Instructs the system to deal damage to an entity.
 * 
 * Note that each event can only deal damage of one type. Use multiple
 * events for multiple types of damage, but be aware that armor effects
 * may be applied separately for each.
 */
trait DamageEvent extends Event {
  
  /**
   * How much damage that should be applied. An interval that goes from
   * 0+ and up (exclusive).
   */
  def variableAmount: Range
  
  /**
   * Sets a new damage amount to be applied. Armor effects make use of this
   * to modify the event.
   */
  def variableAmount_=(newAmount: Range)
  
  /**
   * Amount of damage that is guaranteed.
   */
  def fixedAmount: Int
  
  /**
   * Sets a new fixed amount.
   */
  def fixedAmount_=(newAmount: Int)
  
  /**
   * What kind of damage it is (physical/magical etc.)
   */
  def attackType: AttackType
  
  /**
   * Specifies what type of element the damage is being dealt in
   * (fire etc.), if any. Physical attacks are not required to
   * have an element.
   */
  def damageElement: Option[ElementType]
}

/**
 * Configures additional damage event data during creation.
 */
trait DamageEventConfig extends DamageEvent {
  
  def attackType_=(newType: AttackType)
  
  def damageElement_=(newElement: ElementType)
}

class Damage extends DamageEventConfig {
  private var dmgElement: Option[ElementType] = None
  private var dmgAmount = Range(0, 1)
  private var dmgFixed = 0
  
  override def variableAmount = dmgAmount
  
  override def variableAmount_=(newAmount: Range) {
    
    if (newAmount.start < 0 || newAmount.end <= newAmount.start) {
      val start = Math.max(0, newAmount.start)
      val end = Math.max(start + 1, newAmount.end)
      dmgAmount = Range(start, end)
    }
  }
  
  override def fixedAmount = dmgFixed
  
  override def fixedAmount_=(newAmount: Int) {
    dmgFixed = Math.max(0, newAmount)
  }
  
  override var attackType: AttackType = Physical
  
  override def damageElement = dmgElement
  override def damageElement_=(element: ElementType) = dmgElement = Option(element)

  override def copy = {
    val copy = new Damage()
    copy.variableAmount = variableAmount
    copy.fixedAmount = fixedAmount
    copy.attackType = attackType
    
    if (damageElement.isDefined) {
      copy.damageElement = dmgElement.get
    }

    copy
  }
  
  override def +(other: Event): Event = other match {
    case event: DamageEvent => {
      if (event.attackType == attackType && event.damageElement == damageElement) {
        // Fastest to just create a copy
        val result = copy
        
        /* Calculating the new damage is done by adding the two intervals.
         * All damage from 0 until start in the interval is guaranteed, so the
         * combined guaranteed damage will be the start of both intervals
         * added together. Likewise, the upper limit will both end values
         * added.
         */
        val start = event.variableAmount.start + variableAmount.start
        val stop = event.variableAmount.end + variableAmount.end
        result.variableAmount = Range(start, stop)
        
        result
        
      } else {
        this.copy
      }
    }
    
    case _ => this.copy
  }
}